# Author: Allen Hutchison
# Copyright (C) 2006 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#      Unless required by applicable law or agreed to in writing, software
#      distributed under the License is distributed on an "AS IS" BASIS,
#      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#      See the License for the specific language governing permissions and
#      limitations under the License.
#
# This Oaf Package is responsible for text execution and results reporting.
# It comes with a Oaf::Suite object, to manage the suite file.

package Oaf::Harness;
use base qw(Oaf);

use strict;
use warnings;

use Oaf::Suite;
use Oaf::Test;

# new()
#  Create a new instance of an Oaf::Harness. Get all the basics from the Oaf
#  class, and then add a suite object.
sub new {
  my($class, @args) = @_;

  # Create an Oaf object
  my $self = $class->SUPER::new(@args);

  # Add in a suite object
  $self->{'suite'} = Oaf::Suite->new();

  # Add in a results reporting agent. This can be defined
  # like any other variable (config_file, suite_file, command line).
  # If you don't specify one, we'll use the default from Oaf.
  my $rra = $self->get_param('oaf', 'results_reporting_agent',
			     'Oaf::Results::Text');
  if (eval "require $rra") {
    $self->{'rra'} = $rra->new();
  } else {
    # We have to have a results reporting agent, if I can't load yours
    # then we die.
    $self->fatal_error("Couldn't load results reporting agent: $rra $@");
  }

  # Turn it into an Oaf::Harness
  bless $self, $class;

  return $self;
}

# run()
#  This is the main work method of the Oaf::Harness class.
#  Run is responsible for iterating through a suite object,
#  running each program and storing the results.
sub run {
  my($self) = @_;

  my @results;

  # We've picked up this suite. Mark it as running.
  # the method will throw a fatal_error if there is a
  # problem, and we'll die.
  $self->mark_suite_running();

  while (my $test = $self->pop_test()) {

    # We expect everything that comes off the test
    # stack to be an Oaf::Test object.
    unless(ref $test eq 'Oaf::Test') {
      $self->error("Oaf::Test objects expected on test stack skipping $test");
      next;
    }

    $test->start_time(time);
    my $run_ok = $self->run_program($test);

    if ($run_ok == 0) {
      $self->error("There was a problem running " . $test->test());
      next;
    }

    $test->end_time(time);
    $self->log_results($test);
    push @results, $test;

    # We want to make sure that each test is seperated by at least
    # one second. This will allow results reporting agents to assume
    # that the test start time for each test is unique.
    sleep 1;
  }

  # We're done with this suite. Mark it as complete
  # the method will throw an error if it fails, but
  # we'll continue to execute.
  $self->mark_suite_complete();

  $self->notify($self->summary(\@results));
}

# run_program()
#  Oaf can run any executable on the system. This is the routine
#  that will make it happen. Pass in a program, and a list of args.
#  you get back a reference to an array of the output, the pid, and
#  the exit status.
sub run_program {
  my($self, $test) = @_;

  # To collect prog output
  my @output;

  $self->info("Test: " . $test->test() . " Args: " . $test->args());
  my $pid = open PROG, ($test->test() . " " . $test->args() . " |");
  if(!$pid) {
    $self->error("Can't Run Program, " . $test->test() .
		 " with args " . $test->args());
    return 0;
  }
  $test->pid($pid);

  while (<PROG>) {
    chomp;
    my $output_string = $self->time_stamp() . " | $_";
    push @output, $output_string;
    $self->info($output_string);
  }
  close(PROG);

  $test->exit_code($?);
  $self->info("Exit Status: $?");
  $test->output(\@output);
  return 1;
}


# Wrappers for the embedded suite file.
# full definitions for these routines are
# in Oaf::Suite with the same name.
my %suite_passthrough = map {$_ => 1} qw/has_tests pop_test push_test 
				       mark_suite_running mark_suite_complete/;

# Wrappers for the embedded results reporting
# agent. Full definitions for these routines
# are in Oaf::Results:: with the same name.
my %rra_passthrough = map {$_ => 1} qw/log_results summary notify/;

sub AUTOLOAD {
  my($self, @args) = @_;
  my $method = our $AUTOLOAD;
  $method =~ s/.*:://;
  $self->debug("Method is $method");
  return unless $method =~ /[^A-Z]/; # skip DESTROY and all-cap methods
  if (defined($suite_passthrough{$method})) {
    return $self->{'suite'}->$method(@args);
  } elsif (defined($rra_passthrough{$method})) {
    return $self->{'rra'}->$method(@args);
  } else {
    $self->warning("Calling unknown method $method");
  }
}

# Have to eval to something
1;
__END__
